package oracletojbpmwf.wizards;

import utility.WorkflowConverter;

import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.core.runtime.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;

import java.io.*;

import org.eclipse.ui.*;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
import org.eclipse.ui.part.ISetSelectionTarget;

/**
 * This is a sample new wizard. Its role is to create a new file resource in the
 * provided container. If the container resource (a folder or a project) is
 * selected in the workspace when the wizard is opened, it will accept it as the
 * target container. The wizard creates one file with the extension "xml". If a
 * sample multi-page editor (also available as a template) is registered for the
 * same extension, it will be able to open it.
 */

public class OracleToJbpmWFWizard extends Wizard implements INewWizard {
	boolean canFinish;
	private WizardNewProjectCreationPage mainPage;
	private ConfigureOracleToJbpmWFRuntimePage configureRuntimePage;
	private ConfigureListComponentsWizardPage configureListComponentsWizardPage;

	/**
	 * @return the configureListComponentsWizardPage
	 */
	public ConfigureListComponentsWizardPage getConfigureListComponentsWizardPage() {
		return configureListComponentsWizardPage;
	}

	private OracleToJbpmWFWizardPage oracleToJbpmWFPage;

	/**
	 * @return the oracleToJbpmWFPage
	 */
	public OracleToJbpmWFWizardPage getOracleToJbpmWFPage() {
		return oracleToJbpmWFPage;
	}

	private IProject newProject;
	private IWorkbench workbench;

	public void init(IWorkbench w, IStructuredSelection currentSelection) {
		this.workbench = w;
		setNeedsProgressMonitor(true);
		setWindowTitle("New OracleToJbpmWF Project");
	}

	@Override
	public boolean canFinish() {
		canFinish = configureRuntimePage.locationText.getText() != null
				&& !configureRuntimePage.locationText.getText()
						.equalsIgnoreCase("");
		if (canFinish) {
			try {
				File f = new File(configureRuntimePage.locationText.getText());
				canFinish = f != null && f.exists()
						&& f.getAbsolutePath().endsWith(".wft") && f.isFile();
			} catch (Exception e) {
				canFinish = false;
				
			}
		} else
			canFinish = false;
		return canFinish;
	}

	public void addPages() {
		addMainPage();
		addConfigureRuntimePage();
		// addConfigureListComponentsPage();
	}

	private void addConfigureRuntimePage() {
		configureRuntimePage = new ConfigureOracleToJbpmWFRuntimePage();
		addPage(configureRuntimePage);
	}

	@SuppressWarnings("unused")
	private void addConfigureListComponentsPage() {
		configureListComponentsWizardPage = new ConfigureListComponentsWizardPage();
		addPage(configureRuntimePage);
	}

	private void addMainPage() {
		super.addPages();
		setWindowTitle("New OracleToJbpmWF Project");
		mainPage = new WizardNewProjectCreationPage(
				"basicNewOracleToJbpmWFProjectPage");
		mainPage.setTitle("OracleToJbpmWF Project");
		mainPage.setDescription("Create a new OracleToJbpmWF project.");
		addPage(mainPage);
	}

	private IProject createNewProject() {
		final IProject newProjectHandle = mainPage.getProjectHandle();
		final IProjectDescription description = createProjectDescription(newProjectHandle);
		WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
			protected void execute(IProgressMonitor monitor)
					throws CoreException {
				createProject(description, newProjectHandle, monitor);
			}
		};
		runProjectCreationOperation(op, newProjectHandle);
		return newProjectHandle;
	}

	@SuppressWarnings("unchecked")
	private void addJRELibraries(IJavaProject javaProject)
			throws JavaModelException {
		ArrayList entries = new ArrayList();
		entries.addAll(Arrays.asList(javaProject.getRawClasspath()));
		entries.addAll(Arrays
				.asList(PreferenceConstants.getDefaultJRELibrary()));
		javaProject.setRawClasspath((IClasspathEntry[]) entries
				.toArray(new IClasspathEntry[entries.size()]), null);
	}

	@SuppressWarnings("unchecked")
	private void addSourceFolders(IJavaProject javaProject)
			throws JavaModelException, CoreException {
		ArrayList entries = new ArrayList();
		entries.addAll(Arrays.asList(javaProject.getRawClasspath()));
		addSourceFolder(javaProject, entries, "src/main/java");
		addSourceFolder(javaProject, entries, "src/main/config");
		addSourceFolder(javaProject, entries, "src/main/jpdl");
		addSourceFolder(javaProject, entries, "src/test/java");
		javaProject.setRawClasspath((IClasspathEntry[]) entries
				.toArray(new IClasspathEntry[entries.size()]), null);
	}

	@SuppressWarnings("unchecked")
	private void addSourceFolder(IJavaProject javaProject, ArrayList entries,
			String path) throws CoreException {
		IFolder folder = javaProject.getProject().getFolder(path);
		createFolder(folder);
		IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(folder);
		entries.add(JavaCore.newSourceEntry(root.getPath()));
	}

	private void createFolder(IFolder folder) throws CoreException {
		IContainer parent = folder.getParent();
		if (parent != null && !parent.exists() && parent instanceof IFolder) {
			createFolder((IFolder) parent);
		}
		folder.create(true, true, null);
	}

	@SuppressWarnings("unused")
	private void createJbpmLibraryContainer(IJavaProject javaProject)
			throws JavaModelException {
		/*
		 * JavaCore.setClasspathContainer( new Path("JBPM/"), new IJavaProject[]
		 * { javaProject }, new IClasspathContainer[] { new
		 * OracleToJbpmWFClasspathContainer(javaProject, new
		 * OracleToJbpmWFInstallation()) }, null);
		 */
	}

	@SuppressWarnings("unused")
	private void addJbpmLibraries(IJavaProject javaProject)
			throws JavaModelException {
		/*
		 * createJbpmLibraryContainer(javaProject); ArrayList entries = new
		 * ArrayList();
		 * entries.addAll(Arrays.asList(javaProject.getRawClasspath()));
		 * entries.add(JavaCore.newContainerEntry(new Path("JBPM/")));
		 * javaProject.setRawClasspath((IClasspathEntry[])entries.toArray(new
		 * IClasspathEntry[entries.size()]), null);
		 */
	}

	private void createOutputLocation(IJavaProject javaProject)
			throws JavaModelException, CoreException {
		IFolder binFolder = javaProject.getProject().getFolder("bin");
		createFolder(binFolder);
		IPath outputLocation = binFolder.getFullPath();
		javaProject.setOutputLocation(outputLocation, null);
	}

	private void addJavaBuilder(IJavaProject javaProject) throws CoreException {
		IProjectDescription desc = javaProject.getProject().getDescription();
		ICommand command = desc.newCommand();
		command.setBuilderName(JavaCore.BUILDER_ID);
		desc.setBuildSpec(new ICommand[] { command });
		javaProject.getProject().setDescription(desc, null);
	}

	private void createJavaProject() {
		try {
			newProject = createNewProject();
			newProject.setPersistentProperty(new QualifiedName(
					"OracleToJbpmWF_Name", "OracleToJbpmWFName"),
					"OracleToJbpmWF");
			IJavaProject javaProject = JavaCore.create(newProject);
			createOutputLocation(javaProject);
			addJavaBuilder(javaProject);
			setClasspath(javaProject);
			createInitialContent(javaProject);
			newProject.build(IncrementalProjectBuilder.FULL_BUILD, null);
		} catch (JavaModelException e) {
			e.printStackTrace();
			ErrorDialog.openError(getShell(), "Problem creating java project",
					null, e.getStatus());
			
		} catch (CoreException e) {
			e.printStackTrace();
			ErrorDialog.openError(getShell(), "Problem creating java project",
					null, e.getStatus());
		} catch (IOException e) {
			e.printStackTrace();
			ErrorDialog.openError(getShell(), "Problem creating java project",
					null, null);
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void createInitialContent(IJavaProject javaProject)
			throws CoreException, JavaModelException, IOException {
		createMessageActionHandler(javaProject);
		createSimpleProcessDefinition(javaProject);
	}

	private void createSimpleProcessDefinition(IJavaProject javaProject)
			throws CoreException, JavaModelException, IOException {
		File wftFile = new File(configureRuntimePage.locationText.getText());
		populateProcessDefinitionFile(wftFile, javaProject);
	}

	private void createMessageActionHandler(IJavaProject javaProject)
			throws JavaModelException, IOException {
		
		 /* String resourceName =
		  "org/jbpm/gd/jpdl/resource/MessageActionHandler.java.template";
		  IFolder folder = javaProject.getProject().getFolder("src/main/java");
		  IPackageFragmentRoot root =
		  javaProject.getPackageFragmentRoot(folder); 
		  IPackageFragment pack =
		  root.createPackageFragment("com.sample.action", true, null);
		  InputStream stream =
		  getClass().getClassLoader().getResourceAsStream(resourceName); byte[]
		  content = readStream(stream);
		  pack.createCompilationUnit("MessageActionHandler.java", new
		  String(content), true, null);*/
		 
	}

	private void setClasspath(IJavaProject javaProject)
			throws JavaModelException, CoreException {
		javaProject.setRawClasspath(new IClasspathEntry[0], null);
		addSourceFolders(javaProject);
		addJRELibraries(javaProject);
		javaProject.getRawClasspath();
	}

	private IProjectDescription createProjectDescription(
			IProject newProjectHandle) {
		IPath newPath = null;
		if (!mainPage.useDefaults())
			newPath = mainPage.getLocationPath();
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IProjectDescription description = workspace
				.newProjectDescription(newProjectHandle.getName());
		description.setLocation(newPath);
		addJavaNature(description);
		return description;
	}

	@SuppressWarnings("unchecked")
	private void addJavaNature(IProjectDescription description) {
		ArrayList natures = new ArrayList();
		natures.addAll(Arrays.asList(description.getNatureIds()));
		natures.add(JavaCore.NATURE_ID);
		description.setNatureIds((String[]) natures.toArray(new String[natures
				.size()]));
	}

	private void runProjectCreationOperation(WorkspaceModifyOperation op,
			IProject newProjectHandle) {
		try {
			getContainer().run(false, true, op);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			Throwable t = e.getTargetException();
			if (t instanceof CoreException) {
				handleCoreException(newProjectHandle, (CoreException) t);
			} else {
				handleOtherProblem(t);
			}
		}
	}

	private void handleOtherProblem(Throwable t) {
		MessageDialog.openError(getShell(), "Creation Problems",
				"Internal error: " + t.getMessage());
	}

	private void handleCoreException(final IProject newProjectHandle,
			CoreException e) {
		if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
			MessageDialog
					.openError(
							getShell(),
							"Creation Problems",
							"The underlying file system is case insensitive. There is an existing project which conflicts with '"
									+ newProjectHandle.getName() + "'.");
		} else {
			ErrorDialog.openError(getShell(), "Creation Problems", null, e
					.getStatus());
		}
	}

	void createProject(IProjectDescription description, IProject projectHandle,
			IProgressMonitor monitor) throws CoreException,
			OperationCanceledException {
		try {
			monitor.beginTask("", 2000);
			projectHandle.create(description, new SubProgressMonitor(monitor,
					1000));
			if (monitor.isCanceled()) {
				throw new OperationCanceledException();
			}
			projectHandle.open(IResource.BACKGROUND_REFRESH,
					new SubProgressMonitor(monitor, 1000));
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			monitor.done();
		}
	}

	public IProject getNewProject() {
		return newProject;
	}

	public boolean performFinish() {
		boolean b = configureRuntimePage.updateMessage();
		if (!b) {
			this.getContainer().updateButtons();
		}
		getContainer().updateButtons();
		createJavaProject();
		if (newProject == null) {
			getContainer().updateButtons();
			return false;
		}
		// updatePerspective();
		selectAndReveal(newProject);
		getContainer().updateButtons();
		return true;
	}

	private void populateProcessDefinitionFile(File wftFile, IJavaProject javaProject)
			throws FileNotFoundException, IOException, CoreException {
		Map<String, byte[]> fileMap = WorkflowConverter
				.getWorkflowInputStream(wftFile);

		// TODO : This map contains all the directories and the files to be
		// created. There is one to one mapping
		
		String dirLocation = System.getProperty("java.io.tmpdir");
		
		for(String key : fileMap.keySet()){
			IFolder folder = javaProject.getProject().getFolder(
			"src/main/jpdl/" + key);
			if (!folder.exists()) {
				folder.create(true, true, null);
			}
			IFile file;
			file = folder.getFile("processdefinition.xml");
			if (!file.exists()) {
				byte[] fileByteArr = fileMap.get(key);
				File dir = new File(dirLocation + "\\" + key);
				if (!dir.exists()) {
					dir.mkdir();
				}
				File f = new File(dir.getAbsolutePath() + "\\processdefinition.xml");
				if (!f.exists()) {
					f.createNewFile();
				}
				FileOutputStream fw = new FileOutputStream(f);
				fw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".getBytes());
				fw.write(fileByteArr);
				fw.close();
				file.create(new FileInputStream(f), true, null);
				f.delete();
				dir.delete();
			}
		}
	}

	protected void updatePerspective() {
		try {
			IWorkbenchPage page = PlatformUI.getWorkbench()
					.getActiveWorkbenchWindow().getActivePage();
			if (page.findView("org.eclipse.ui.views.PropertySheet") == null) {
				page.showView("org.eclipse.ui.views.PropertySheet");
			}
		} catch (PartInitException e) {
			e.printStackTrace();
		}
	}

	protected void selectAndReveal(IResource newResource) {
		selectAndReveal(newResource, workbench.getActiveWorkbenchWindow());
	}

	@SuppressWarnings("unchecked")
	private void selectAndReveal(IResource resource, IWorkbenchWindow window) {
		if (!inputValid(resource, window))
			return;
		Iterator itr = getParts((window.getActivePage())).iterator();
		while (itr.hasNext()) {
			selectAndRevealTarget(window, new StructuredSelection(resource),
					getTarget((IWorkbenchPart) itr.next()));
		}
	}

	private boolean inputValid(IResource resource, IWorkbenchWindow window) {
		if (window == null || resource == null)
			return false;
		else if (window.getActivePage() == null)
			return false;
		else
			return true;
	}

	private void selectAndRevealTarget(IWorkbenchWindow window,
			final ISelection selection, ISetSelectionTarget target) {
		if (target == null)
			return;
		final ISetSelectionTarget finalTarget = target;
		window.getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				finalTarget.selectReveal(selection);
			}
		});
	}

	private ISetSelectionTarget getTarget(IWorkbenchPart part) {
		ISetSelectionTarget target = null;
		if (part instanceof ISetSelectionTarget) {
			target = (ISetSelectionTarget) part;
		} else {
			target = (ISetSelectionTarget) part
					.getAdapter(ISetSelectionTarget.class);
		}
		return target;
	}

	@SuppressWarnings("unchecked")
	private List getParts(IWorkbenchPage page) {
		List result = new ArrayList();
		addParts(result, page.getViewReferences());
		addParts(result, page.getEditorReferences());
		return result;
	}

	@SuppressWarnings("unchecked")
	private void addParts(List parts, IWorkbenchPartReference[] refs) {
		for (int i = 0; i < refs.length; i++) {
			IWorkbenchPart part = refs[i].getPart(false);
			if (part != null) {
				parts.add(part);
			}
		}
	}

	@SuppressWarnings("unused")
	private byte[] readStream(InputStream in) throws IOException {
		byte[] contents = null;
		int fileSize = 0;
		byte[] buffer = new byte[1024];
		int bytesRead = in.read(buffer);
		while (bytesRead != -1) {
			byte[] newContents = new byte[fileSize + bytesRead];
			if (fileSize > 0) {
				System.arraycopy(contents, 0, newContents, 0, fileSize);
			}
			System.arraycopy(buffer, 0, newContents, fileSize, bytesRead);
			contents = newContents;
			fileSize += bytesRead;
			bytesRead = in.read(buffer);
		}
		return contents;
	}

	/*
	 * 
	 * private OracleToJbpmWFWizardPage page; private ISelection selection;
	 *//**
	 * Constructor for OracleToJbpmWFWizard.
	 */
	/*
	 * public OracleToJbpmWFWizard() { super(); setNeedsProgressMonitor(true); }
	 *//**
	 * Adding the page to the wizard.
	 */
	/*
	 * 
	 * public void addPages() { page = new OracleToJbpmWFWizardPage(selection);
	 * addPage(page); }
	 *//**
	 * This method is called when 'Finish' button is pressed in the wizard.
	 * We will create an operation and run it using wizard as execution context.
	 */
	/*
	 * public boolean performFinish() { final String containerName =
	 * page.getContainerName(); final String fileName = page.getFileName();
	 * IRunnableWithProgress op = new IRunnableWithProgress() { public void
	 * run(IProgressMonitor monitor) throws InvocationTargetException { try {
	 * doFinish(containerName, fileName, monitor); } catch (CoreException e) {
	 * throw new InvocationTargetException(e); } finally { monitor.done(); } }
	 * }; try { getContainer().run(true, false, op); } catch
	 * (InterruptedException e) { return false; } catch
	 * (InvocationTargetException e) { Throwable realException =
	 * e.getTargetException(); MessageDialog.openError(getShell(), "Error",
	 * realException.getMessage()); return false; } return true; }
	 *//**
	 * The worker method. It will find the container, create the file if
	 * missing or just replace its contents, and open the editor on the newly
	 * created file.
	 */
	/*
	 * 
	 * private void doFinish( String containerName, String fileName,
	 * IProgressMonitor monitor) throws CoreException { // create a sample file
	 * monitor.beginTask("Creating " + fileName, 2); IWorkspaceRoot root =
	 * ResourcesPlugin.getWorkspace().getRoot(); IResource resource =
	 * root.findMember(new Path(containerName)); if (!resource.exists() ||
	 * !(resource instanceof IContainer)) { throwCoreException("Container \"" +
	 * containerName + "\" does not exist."); } IContainer container =
	 * (IContainer) resource; final IFile file = container.getFile(new
	 * Path(fileName)); try { InputStream stream = openContentStream(); if
	 * (file.exists()) { file.setContents(stream, true, true, monitor); } else {
	 * file.create(stream, true, monitor); } stream.close(); } catch
	 * (IOException e) { } monitor.worked(1);
	 * monitor.setTaskName("Opening file for editing...");
	 * getShell().getDisplay().asyncExec(new Runnable() { public void run() {
	 * IWorkbenchPage page =
	 * PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); try
	 * { IDE.openEditor(page, file, true); } catch (PartInitException e) { } }
	 * }); monitor.worked(1); }
	 *//**
	 * We will initialize file contents with a sample text.
	 */
	/*
	 * 
	 * private InputStream openContentStream() { String contents =
	 * "This is the initial file contents for *.xml file that should be word-sorted in the Preview page of the multi-page editor"
	 * ; return new ByteArrayInputStream(contents.getBytes()); }
	 * 
	 * private void throwCoreException(String message) throws CoreException {
	 * IStatus status = new Status(IStatus.ERROR, "OracleToJbpmWF", IStatus.OK,
	 * message, null); throw new CoreException(status); }
	 *//**
	 * We will accept the selection in the workbench to see if we can
	 * initialize from it.
	 * 
	 * @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection)
	 */
	/*
	 * public void init(IWorkbench workbench, IStructuredSelection selection) {
	 * this.selection = selection; }
	 */
}